#include <UnitTest++/UnitTest++.h>

#include <mysql++/mysql++.h>



// Import some stuff for easy testing
using mysqlpp::Connection ;
using mysqlpp::ConnectionFailed ;
using mysqlpp::Query ;
using mysqlpp::SimpleResult ;
using mysqlpp::StoreQueryResult ;


//  Some globals to use for our testing
const std::string   db_name( "test_shopper" ) ;
const std::string   db_create( "create database " + db_name ) ;
const std::string   db_drop( "drop database " + db_name ) ;
const std::string   stock_describe( "describe stock" ) ;
const std::string   stock_select( "select * from stock" ) ;
const std::string   stock_update( "update stock set quantity = quantity - 1" ) ;

const std::string   items_table =
  " create table items "
  " ( "
  "   post_date           timestamp       NOT NULL , "
  "   name                varchar(254)    NOT NULL "
  " ) " ;

const std::string   stock_table = 
  " create table stock "
  " ( "
  " catalog_num           char(4)         NOT NULL , "
  " name                  varchar(254)    NOT NULL , "
  " quantity              integer         NOT NULL "
  " ) " ;


struct Connection_Fixture
{
  Connection_Fixture() : m_conn( db_name.c_str() ) , m_query( & m_conn ) , m_flag( false ) {}

  Connection    m_conn ;
  SimpleResult  m_result ;
  Query         m_query ;
  bool          m_flag ;

} ; /* struct Connection_Fixture */



// This sets up the test database so we can connect to it and makes sure we have
// basic database access before going any further
TEST( DB_Create )
{
  bool          flag ;
  Connection    conn ;
  SimpleResult  result ;

  conn.connect() ;
  flag = conn.connected() ;

  CHECK( flag ) ;

  Query         query   = conn.query( db_create ) ;

  result = query.execute() ;

  CHECK( result ) ;

} /* TEST( DB_Create ) */


// Test to make sure we can get a connection
TEST_FIXTURE( Connection_Fixture, Connection )
{
  // Check to see if the parent fixture made the connection properly
  bool        m_flag = m_conn.connected() ;

  CHECK(m_flag);

  // Check to see that logging into the root database fails
  CHECK_THROW( m_conn.connect( "mysql" ) , ConnectionFailed ) ;

  // Connect to the test database
  m_conn.connect( "test" ) ;
  m_flag = m_conn.connected() ;

  CHECK(m_flag);

  m_conn.disconnect() ;
  m_flag = m_conn.connected() ;

  CHECK( !m_flag ) ;

} /* TEST_FIXTURE( Connection_Fixture, Connection ) */


TEST_FIXTURE( Connection_Fixture , Creation )
{
  m_flag = m_conn.connected() ;

  CHECK( m_flag );

  m_query  = m_conn.query( "drop database " + db_name ) ;
  m_result = m_query.execute() ;
  m_result = m_query.execute( "create database " + db_name ) ;

  CHECK( m_result ) ;

  m_flag = m_conn.select_db( db_name ) ;

  CHECK( m_flag );

  m_result = m_query.execute( items_table ) ;

  CHECK( m_result ) ;

  m_result = m_query.execute( stock_table ) ;

  CHECK( m_result ) ;

} /* TEST_FIXTURE( Connection_Fixture , Creation ) */


TEST_FIXTURE( Connection_Fixture , Describe )
{
  m_flag = m_conn.select_db( db_name ) ;
  CHECK( m_flag ) ;

  StoreQueryResult  res_set = m_query.store( stock_describe ) ;

  CHECK( res_set ) ;

  // Result should have 6 columns 
  CHECK_EQUAL( 6 , res_set.num_fields() ) ;

  // and three rows
  CHECK_EQUAL( 3 , res_set.num_rows() ) ;

} /* TEST_FIXTURE( Connection_Fixture , Describe ) */


TEST_FIXTURE( Connection_Fixture , Insert )
{
  m_flag = m_conn.select_db( db_name ) ;
  CHECK( m_flag ) ;

  const std::string stock_insert( "insert into stock values( %0Q , %1Q , %2 )" ) ;
  m_query  = m_conn.query( stock_insert ) ;
  m_query.parse() ;

  m_result  = m_query.execute( "AAAA" , "Item One"   , "1" ) ;

  CHECK( m_result ) ;

  m_result  = m_query.execute( "BBBB" , "Item Two"   , "2" ) ;

  CHECK( m_result ) ;

  m_result  = m_query.execute( "CCCC" , "Item Three" , "3" ) ;

  CHECK( m_result ) ;

  m_result  = m_query.execute( "DDDD" , "Item Four"  , "4" ) ;

  CHECK( m_result ) ;

} /* TEST_FIXTURE( Connnection_Fixture , Insert ) */


TEST_FIXTURE( Connection_Fixture , Select )
{
  m_flag = m_conn.select_db( db_name ) ;
  CHECK( m_flag ) ;

  StoreQueryResult  res_set = m_query.store( stock_select ) ;

  CHECK( res_set ) ;

  // Should be three fields and four rows
  CHECK_EQUAL( 3 , res_set.num_fields() ) ;
  CHECK_EQUAL( 4 , res_set.num_rows() ) ;

  for( unsigned i = 0 ; i < 4 ; i++ )
  {
    unsigned  quant = res_set[i][2] ;
    CHECK_EQUAL( i + 1 , quant ) ;

  } /* for */

  m_result = m_query.execute( stock_update ) ;

  CHECK( m_result ) ;

  res_set = m_query.store( stock_select ) ;

  CHECK( res_set ) ;

  // Should be three fields and four rows
  CHECK_EQUAL( 3 , res_set.num_fields() ) ;
  CHECK_EQUAL( 4 , res_set.num_rows() ) ;

  for( unsigned i = 0 ; i < 4 ; i++ )
  {
    unsigned  quant = res_set[i][2] ;
    CHECK_EQUAL( i , quant ) ;

  } /* for */

} /* TEST_FIXTURE( Connection_Fixture , Select ) */


int
main()
{
  return  UnitTest::RunAllTests();

}

